package net.chathu.airgw.model.conn 
{
	import flash.events.NetStatusEvent;
	import flash.events.TimerEvent;
	import flash.net.GroupSpecifier;
	import flash.net.NetConnection;
	import flash.net.NetGroup;
	import flash.utils.Dictionary;
	import flash.utils.SetIntervalTimer;
	import flash.utils.Timer;
	import net.chathu.airgw.AutoUpdater;
	import net.chathu.airgw.Config;
	import net.chathu.airgw.Debug;
	import net.chathu.airgw.model.P2PGroupInfo;
	import net.chathu.airgw.model.P2PUser;
	import net.chathu.airgw.model.SessionData;
	import net.chathu.airgw.packets.gw.serverpackets.EchoPacket;
	import net.chathu.airgw.packets.gw.serverpackets.JoinPacket;
	import net.chathu.airgw.packets.gw.serverpackets.PartPacket;
	import net.chathu.airgw.packets.gw.serverpackets.PrivMsgPacket;
	import net.chathu.airgw.packets.gw.serverpackets.TopicPacket;
	import net.chathu.airgw.Soul;
	/**
	 * ...
	 * @author ze
	 */
	public class P2PChatConnection 
	{
		private var soul:Soul;
		
		private const SERVER:String = "rtmfp://p2p.rtmfp.net/";
		private const DEVKEY:String = "2f873a0247c1ba95016a5474-f52cb63c2320";
		
		private const CMD_TYPE_HELLO:int = 0x01;
		private const CMD_TYPE_USERINFO:int = 0x02;
		private const CMD_TYPE_MESSAGE:int = 0x03;
		private const CMD_TYPE_PING:int = 0x04;
		private const CMD_TYPE_BYE:int = 0x05;
		private const CMD_TYPE_UPDATENOTIFY:int = 0x06;
		
		
		//[Bindable]
		private var userName:String;
				
		//[Bindable]
		//private var connected:Boolean = false;
				
		private var netConnection:NetConnection;
		
		
		//TODO: store groups!
		
		//TODO: add encryption/digital sign. Igy barki irhat barki neveben!
		private var pingTimer:Timer;
		
		public function P2PChatConnection(soul:Soul) 
		{
			//Debug.echo("P2P connection init");
			this.soul = soul;
			connect();
			
			pingTimer = new Timer(10000);
			pingTimer.addEventListener(TimerEvent.TIMER, onPingTimerEvent);
			pingTimer.start();
						
		}
					
		private function connect():void
		{
			netConnection = new NetConnection();
			netConnection.addEventListener(NetStatusEvent.NET_STATUS,netStatus);				
			netConnection.connect(SERVER + DEVKEY);
		}
		
		private function netStatus(event:NetStatusEvent):void
		{
			//TODO: ha bezarodik az irc kapcsolat, nem tudunk uzenetet kuldeni ra, raadasul null lesz a soul.ircConnection!
			//soul.ircConnection.sendPacket(new EchoPacket(Config.GW_ECHO_NICK, "NetStatus: " + event.info.code)); 
			
			//Debug.echo(event.info.code);
			var sessionData:SessionData = soul.sessionData;
			var group:NetGroup;
			var groupName:String;
			var groupInfo:P2PGroupInfo;
			
			switch(event.info.code){
				case "NetConnection.Connect.Success":
					//setupGroup();
					if(Config.P2P_AUTOJOIN_CHANNEL) {
						joinGroup(Config.P2P_AUTOJOIN_CHANNEL);
					}

				break;
				case "NetGroup.Neighbor.Connect":
					group = NetGroup(event.target);
					groupName = sessionData.getGroupName(group);
					groupInfo = sessionData.getP2PGroupInfo(groupName);
					//minden connectnel el kell kuldeni? - attol fugg, hany connect van!
					sendHello(groupName, soul.sessionData.nick);
					
					groupInfo.neighbors++;
					//soul.ircConnection.sendPacket(new TopicPacket(Config.GW_ECHO_NICK,groupName, "P2P neighbors: " + groupInfo.neighbors));
				break;
				case "NetGroup.Neighbor.Disconnect":
					//valaki  (nem te) kilepett a groupbol
					group = NetGroup(event.target);
					groupName = sessionData.getGroupName(group);
					groupInfo = sessionData.getP2PGroupInfo(groupName);
					//minden connectnel el kell kuldeni? - attol fugg, hany connect van!
					//sendHello(groupName, soul.sessionData.nick);
					
					groupInfo.neighbors--;
					//soul.ircConnection.sendPacket(new TopicPacket(Config.GW_ECHO_NICK,groupName, "P2P neighbors: " + groupInfo.neighbors));
				break;
				case "NetGroup.Posting.Notify":
					handleGroupCommand(NetGroup(event.target),event.info.message);
				break;
				case "NetGroup.Connect.Success":
					//connected = true;
					group = event.info.group;
					groupName = sessionData.getGroupName(group);
					soul.ircConnection.sendPacket(new JoinPacket(soul.sessionData.yourHost,groupName));
				break;
				case "NetGroup.Connect.Closed":
					//kileptel egy groupbol
					group = event.info.group;
					groupName = sessionData.getGroupName(group);
					Debug.echo("Netgroup.connect.closed..");
				break;
			}
		}
		
		public function joinGroup(groupName:String):void {
			if (soul.sessionData.isInGroup(groupName))
			{
				Debug.echo("Error, you are already in this group: " + groupName);
			} else {
				var spec:GroupSpecifier = new GroupSpecifier("channels/" + groupName);
				spec.postingEnabled = true;
				spec.serverChannelEnabled = true;
				//Debug.echo("SPEC: " + spec.groupspecWithAuthorizations());
					
				var group:NetGroup = new NetGroup(netConnection, spec.groupspecWithAuthorizations());
				var groupInfo:P2PGroupInfo = new P2PGroupInfo(group);
				//groupInfos[groupName] = groupInfo;
				soul.sessionData.addP2PGroupInfo(groupName, groupInfo);
				group.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
			}	
		}
		
		public function leaveGroup(groupName:String):void {
			if (soul.sessionData.isInGroup(groupName))
			{
				/*var group:NetGroup = soul.sessionData.getP2PGroupInfo(groupName).netGroup;
				var message:Object = new Object();
				message.type = CMD_TYPE_BYE;
				message.sender = group.convertPeerIDToGroupAddress(netConnection.nearID);
				group.post(message);*/
				sendBye(groupName);
				
				//group.close(); //ez nem jo..nem kuldi el a BYE uzenetet..
				//delete groupInfos[groupName];
				soul.sessionData.removeP2PGroupInfo(groupName);
				//TODO: torolni a felhasznalokat, torolni a kilepett felhasznalot!
				//soul.sessionData.removeUneccessaryUserData();
			} else {
				Debug.echo("Error, no such group: " + groupName);
			}	
		}
		
		
		public function sendMessage(groupName:String, txt:String):void {
			var message:Object = new Object();
			message.type = CMD_TYPE_MESSAGE;
			message.text = txt;
			sendMessageToGroup(groupName, message);
		}
		
		/*
		 * userName: milyen neven lassanak a felhasznalok. Barmilyen hiba eseten generalt nevet latnak a tobbiek (ebbol generalodik).
		 */
		private function sendHello(groupName:String, userName:String):void
		{
			var message:Object = new Object();
			message.type = CMD_TYPE_HELLO;
			message.userName = userName;
			sendMessageToGroup(groupName, message);
		}
		
		private function sendPing(groupName:String):void
		{
			var message:Object = new Object();
			message.type = CMD_TYPE_PING;
			message.seed = Math.random();
			sendMessageToGroup(groupName, message);
		}
		
		public function sendUpdateNotify(groupName:String):void
		{
			var message:Object = new Object();
			message.type = CMD_TYPE_UPDATENOTIFY;
			sendMessageToGroup(groupName, message);

		}
		
		private function sendBye(groupName:String):void
		{
			var message:Object = new Object();
			message.type = CMD_TYPE_BYE;
			sendMessageToGroup(groupName, message);
			
		}
		
		//Lehet-e fixen egy embernek kuldeni?
		private function sendUserInfo(groupName:String):void
		{
			var message:Object = new Object();
			message.type = CMD_TYPE_USERINFO;
			message.userName = soul.sessionData.nick;
			sendMessageToGroup(groupName, message);
			
		}
		
		private function sendMessageToGroup(groupName:String, message:Object):void {
			try {
				if (soul.sessionData.isInGroup(groupName))
				{
					var group:NetGroup = soul.sessionData.getP2PGroupInfo(groupName).netGroup;
					message.sender = group.convertPeerIDToGroupAddress(netConnection.nearID);
					group.post(message);
				} else {
					Debug.echo("Not in group: " + groupName);
				}
			} catch (err:Error) {
				trace("error: " + err);
				close();
			}
			
		}
		
		private function handleGroupCommand(group:NetGroup, messageObj:Object):void {
			if (!soul.sessionData)
			{
				//ha azutan jott egy csomag, hogy destroyoltuk a soult, akkor ne kezeljuk ezt;
				return;
			}
			
			var type:int = int(messageObj.type);
			var groupName:String = soul.sessionData.getGroupName(group);
			
			var sender:String = messageObj.sender;
			var user:P2PUser = P2PUser(soul.sessionData.p2pUserList.getUser(sender));
			//soul.ircConnection.sendPacket(new EchoPacket(Config.GW_ECHO_NICK, "Command: " + type +" from: " + (user ? user.name : "UNKNOWN"))); 
			if (!groupName) {
				Debug.echo("Error, missing group name!");
				return;
			}
			var groupInfo:P2PGroupInfo = soul.sessionData.getP2PGroupInfo(groupName);
			
			switch (type)
			{
				case CMD_TYPE_HELLO:
				case CMD_TYPE_USERINFO:
					if (!user)
					{
						var requestedNick:String = messageObj.userName;
						//ha meg nem volt errol a felhasznalorol infonk:
						user = new P2PUser(sender, requestedNick);
						soul.sessionData.p2pUserList.add(user, false); //itt valtozhat a nickje esetleg!
						//azert kell egy nagy userlist, hogy a kulonbozo szobakban is ugyanazon a nicken legyen elerheto ugyanaz a felhasznalo
						groupInfo.addUser(user.id);
						soul.ircConnection.sendPacket(new JoinPacket(user.userHost, groupName));
					} else if(!groupInfo.hasUser(user.id)) {
						//kilepett, majd visszalepett egy user:
						groupInfo.addUser(user.id);
						soul.ircConnection.sendPacket(new JoinPacket(user.userHost, groupName));
					}
					if (type == CMD_TYPE_HELLO)
					{
						sendUserInfo(groupName);
					}
				break;
				case CMD_TYPE_MESSAGE:
					var message:String = messageObj.text;
					if (user)
					{
						//Debug.echo("NOTIFY: room: " + event.info.message.groupName + " msg: " + event.info.message.text);
						soul.ircConnection.sendPacket(new PrivMsgPacket(user.userHost, groupName, message));
					}
				break;
				case CMD_TYPE_PING:
					if (user)
					{
						trace("Ping from " + user.name);
						user.updateLastPingTime();
					}
				break;
				case CMD_TYPE_UPDATENOTIFY:
					if (user)
					{
						Debug.echo("Update notify from " + user.name);
						AutoUpdater.check(null);
					}
				break;
				case CMD_TYPE_BYE:
					if (user)
					{
						onUserLeftGroup(groupName, user);
					}
				break;
			}
			
		}
		
		private function onUserLeftGroup(groupName:String, user:P2PUser):void {
			var groupInfo:P2PGroupInfo = soul.sessionData.getP2PGroupInfo(groupName);
			if (groupInfo)
			{
				groupInfo.removeUser(user.id);
				soul.ircConnection.sendPacket(new PartPacket(user.userHost, groupName));
			}	
		}
		
		
		private function onPingTimerEvent(...e):void {
			//var groups:Array = soul.sessionData.getJoinedGroupNames();
			var groups:Dictionary = soul.sessionData.getJoinedGroupInfos();
			for (var groupName:String in groups)
			//for (var i:int = 0; i < groups.length; i++)
			{
				//trace("Sending ping to : " + groups[i]);
				trace("Sending ping to : " + groupName);
				sendPing(groupName);
			}
			
			checkUserTimeouts();
		}
		private function checkUserTimeouts():void {
			
			var currentTime:Number = new Date().getTime();
			for each(var p2pUser:P2PUser in soul.sessionData.p2pUserList.getDictionary())
			{
				if (currentTime - p2pUser.lastPingDate.getTime() > 30000)
				{
					//trace("User left: " + p2pUser.name);
					var userInRooms:Array = soul.sessionData.getP2PGroupInfosByUserId(p2pUser.id);
					for each (var groupInfo:P2PGroupInfo in userInRooms) {
						var groupName:String = soul.sessionData.getGroupName(groupInfo.netGroup);
						onUserLeftGroup(groupName, p2pUser);
					}
					//remove from userlist:
					soul.sessionData.p2pUserList.remove(p2pUser);
				}
			}
		}
		
		public function get connected():Boolean {
			return netConnection.connected;
		}
		
		public function close():void {
			pingTimer.stop();			
			netConnection.close();
		}

	}
}